// Copyright (C) 1995-1998 by Symantec
// Copyright (C) 2000-2009 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
 * This source file is made available for personal use
 * only. The license is in /dmd/src/dmd/backendlicense.txt
 * For any other uses, please contact Digital Mars.
 */

#if !SPP

#include	<stdio.h>
#include	<string.h>
#include	<time.h>
#include	"cc.h"
#include	"el.h"
#include	"oper.h"
#include	"code.h"
#include	"global.h"
#include	"type.h"

static char __file__[] = __FILE__;	/* for tassert.h		*/
#include	"tassert.h"

STATIC void pe_add(block *b);
STATIC int need_prolog(block *b);

/********************************************************
 * Determine which blocks get the function prolog and epilog
 * attached to them.
 */

void cod5_prol_epi()
{   tym_t tym;
    tym_t tyf;
    block *b;
    block *bp;
    list_t bl;
    int nepis;

    tyf = funcsym_p->ty();
    tym = tybasic(tyf);

goto L1;
    if (!(config.flags4 & CFG4optimized) ||
	anyiasm ||
	usedalloca ||
	usednteh ||
	tyf & (mTYnaked | mTYloadds) ||
	tym == TYifunc ||
	tym == TYmfunc ||	// can't yet handle ECX passed as parameter
	tym == TYjfunc ||	// can't yet handle EAX passed as parameter
	config.flags & (CFGalwaysframe | CFGtrace) ||
//	config.fulltypes ||
	(config.wflags & WFwindows && tyfarfunc(tym)) ||
	need_prolog(startblock)
       )
    {	// First block gets the prolog, all return blocks
	// get the epilog.
	//printf("not even a candidate\n");
    L1:
	cod5_noprol();
	return;
    }

    // Turn on BFLoutsideprolog for all blocks outside the ones needing the prolog.

    for (b = startblock; b; b = b->Bnext)
	b->Bflags &= ~BFLoutsideprolog;			// start with them all off

    pe_add(startblock);

    // Look for only one block (bp) that will hold the prolog
    bp = NULL;
    nepis = 0;
    for (b = startblock; b; b = b->Bnext)
    {	int mark;

	if (b->Bflags & BFLoutsideprolog)
	    continue;

	// If all predecessors are marked
	mark = 0;
	assert(b->Bpred);
	for (bl = b->Bpred; bl; bl = list_next(bl))
	{
	    if (list_block(bl)->Bflags & BFLoutsideprolog)
	    {
		if (mark == 2)
		    goto L1;
		mark = 1;
	    }
	    else
	    {
		if (mark == 1)
		    goto L1;
		mark = 2;
	    }
	}
	if (mark == 1)
	{
	    if (bp)		// if already have one
		goto L1;
	    bp = b;
	}

	// See if b is an epilog
	mark = 0;
	for (bl = b->Bsucc; bl; bl = list_next(bl))
	{
	    if (list_block(bl)->Bflags & BFLoutsideprolog)
	    {
		if (mark == 2)
		    goto L1;
		mark = 1;
	    }
	    else
	    {
		if (mark == 1)
		    goto L1;
		mark = 2;
	    }
	}
	if (mark == 1 || b->BC == BCret || b->BC == BCretexp)
	{   b->Bflags |= BFLepilog;
	    nepis++;
	    if (nepis > 1 && config.flags4 & CFG4space)
		goto L1;
	}
    }
    if (bp)
    {	bp->Bflags |= BFLprolog;
	//printf("=============== prolog opt\n");
    }
}

/**********************************************
 * No prolog/epilog optimization.
 */

void cod5_noprol()
{
    block *b;

    //printf("no prolog optimization\n");
    startblock->Bflags |= BFLprolog;
    for (b = startblock; b; b = b->Bnext)
    {
	b->Bflags &= ~BFLoutsideprolog;
	switch (b->BC)
	{   case BCret:
	    case BCretexp:
		b->Bflags |= BFLepilog;
		break;
	    default:
		b->Bflags &= ~BFLepilog;
	}
    }
}

/*********************************************
 * Add block b, and its successors, to those blocks outside those requiring
 * the function prolog.
 */

STATIC void pe_add(block *b)
{   list_t bl;

    if (b->Bflags & BFLoutsideprolog ||
	need_prolog(b))
	return;

    b->Bflags |= BFLoutsideprolog;
    for (bl = b->Bsucc; bl; bl = list_next(bl))
	pe_add(list_block(bl));
}

/**********************************************
 * Determine if block needs the function prolog to be set up.
 */

STATIC int need_prolog(block *b)
{
    if (b->Bregcon.used & fregsaved)
	goto Lneed;

    // If block referenced a param in 16 bit code
    if (!I32 && b->Bflags & BFLrefparam)
	goto Lneed;

    // If block referenced a stack local
    if (b->Bflags & BFLreflocal)
	goto Lneed;

    return 0;

Lneed:
    return 1;
}

#endif
